//*****************************************************************************
//
// Copyright, (C) 2000-2005 Quantic Dream SA
//
// These coded instructions, statements and computer programs contain
// unpublished information, proprietary to Quantic Dream SA and are 
// protected by French and EEC copyright laws. They may not be 
// disclosed to third parties or copied or duplicated, in whole or in part, 
// without prior written consent of Quantic Dream SA
//
// Unpublished-rights reserved under the Copyright Laws of the EEC.
//
//*****************************************************************************
//
//	CLASS:	QtRenderWidget
//
//	JJA : 2012-07-23 - Created
//*****************************************************************************

#include <gl/glew.h>

#include "QtRenderWidget.h"
#include "Mesh/Mesh.h"
#include "Mesh/MeshInstance.h"
#include "Shaders/ColorPerVertexShader.h"
#include "Shaders/GouraudShader.h"
#include "Camera/Camera.h"

#include <QFile>
#include <QMessageBox>
#include <QKeyEvent>
#include <QDebug>

QByteArray QtRenderWidget::_TriangleMeshData;
QByteArray QtRenderWidget::_PlaneMeshData;

float QtRenderWidget::_fCameraTranslationSpeed	= 10.0f;
float QtRenderWidget::_fCameraRotationSpeed		= 0.3f;

//=============================================================================
//	CODE STARTS HERE
//=============================================================================

//-----------------------------------------------------------------------------
//! @brief		QtRenderWidget constructor
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
QtRenderWidget::QtRenderWidget(const QGLFormat &	OpenGLFormat, 
							   QWidget *			pParent)
: QGLWidget					( OpenGLFormat, pParent )
, _MainLoopTimer			( this )
, _nCameraDeltaPitch		( 0 )
, _nCameraDeltaHead			( 0 )
, _vCameraTranslationVector	( 0.0f, 0.0f, 0.0f )
, _uiFramesCount			( 0 )
, _uiFpsTimer				( 0 )
{
	setFocusPolicy( Qt::ClickFocus );
}

//-----------------------------------------------------------------------------
//! @brief		CreateTriangleMeshData
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void QtRenderWidget::CreateTriangleMeshData()
{
	QDataStream MeshDataStream( &_TriangleMeshData, QIODevice::ReadWrite );

	MeshDataStream.setByteOrder( QDataStream::LittleEndian );
	MeshDataStream.setFloatingPointPrecision( QDataStream::SinglePrecision );

	// Vertices
	MeshDataStream << 3;

	MeshDataStream << -1.0f << 0.0f << 0.0f << 1.0f;
	MeshDataStream << 1.0f << 0.0f << 0.0f << 1.0f;

	MeshDataStream << 1.0f << 0.0f << 0.0f << 1.0f;
	MeshDataStream << 0.0f << 1.0f << 0.0f << 1.0f;

	MeshDataStream << 0.0f << 2.0f << 0.0f << 1.0f;
	MeshDataStream << 0.0f << 0.0f << 1.0f << 1.0f;
}

//-----------------------------------------------------------------------------
//! @brief		CreatePlaneMeshData
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void QtRenderWidget::CreatePlaneMeshData()
{
	QDataStream MeshDataStream( &_PlaneMeshData, QIODevice::ReadWrite );

	MeshDataStream.setByteOrder( QDataStream::LittleEndian );
	MeshDataStream.setFloatingPointPrecision( QDataStream::SinglePrecision );

	// Vertices
	MeshDataStream << 6;

	MeshDataStream << -10.0f << 0.0f << -10.0f << 1.0f;
	MeshDataStream << 0.6f << 0.6f << 0.6f << 1.0f;

	MeshDataStream << -10.0f << 0.0f << 10.0f << 1.0f;
	MeshDataStream << 0.6f << 0.6f << 0.6f << 1.0f;

	MeshDataStream << 10.0f << 0.0f << 10.0f << 1.0f;
	MeshDataStream << 0.6f << 0.6f << 0.6f << 1.0f;

	MeshDataStream << -10.0f << 0.0f << -10.0f << 1.0f;
	MeshDataStream << 0.6f << 0.6f << 0.6f << 1.0f;

	MeshDataStream << 10.0f << 0.0f << 10.0f << 1.0f;
	MeshDataStream << 0.6f << 0.6f << 0.6f << 1.0f;

	MeshDataStream << 10.0f << 0.0f << -10.0f << 1.0f;
	MeshDataStream << 0.6f << 0.6f << 0.6f << 1.0f;
}

//-----------------------------------------------------------------------------
//! @brief		initializeGL
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void QtRenderWidget::initializeGL()
{
	glClearColor( 0.2f, 0.2f, 0.2f, 1.0f );
	
	// Initialize glew library
	glewExperimental = GL_TRUE;
	GLenum eResult = glewInit();

	glEnable( GL_DEPTH_TEST );
	glDepthMask( GL_TRUE );
	glDepthFunc( GL_LEQUAL );
	glDepthRange( 0.0f, 1.0f );

	// Load triangle primitive mesh data
	MESH* pMesh = new MESH();
	//pMesh->Load( &_TriangleMeshData ); 
	pMesh->LoadObjFile( "../../../Models/Triangle.obj" );
	/*pMesh->GetMeshVertex( 0 )->_vColor = VECTOR4( 1.0f, 0.0f, 0.0f, 1.0f );
	pMesh->GetMeshVertex( 1 )->_vColor = VECTOR4( 0.0f, 1.0f, 0.0f, 1.0f );
	pMesh->GetMeshVertex( 2 )->_vColor = VECTOR4( 0.0f, 0.0f, 1.0f, 1.0f );*/
	_lpMeshes.push_back( pMesh );

	// Load shaders
	_pShaderArray[SHADER_ID_COLOR_PER_VERTEX] = new COLOR_PER_VERTEX_SHADER();
	_pShaderArray[SHADER_ID_COLOR_PER_VERTEX]->Load( "../../../Shaders/glsl/ColorPerVertex_vertex_shader.glsl", "../../../Shaders/glsl/ColorPerVertex_pixel_shader.glsl" );

	_pShaderArray[SHADER_ID_GOURAUD] = new GOURAUD_SHADER();
	_pShaderArray[SHADER_ID_GOURAUD]->Load( "../../../Shaders/glsl/Gouraud_vertex_shader.glsl", "../../../Shaders/glsl/Gouraud_pixel_shader.glsl" );

	// Create triangle instances
	for	( int i = 0; i < 5; i++ )
	{
		MESH_INSTANCE* pMeshInstance = new MESH_INSTANCE( pMesh );

		pMeshInstance->SetShader( _pShaderArray[SHADER_ID_COLOR_PER_VERTEX] );
		//pMeshInstance->SetShader( _pShaderArray[SHADER_ID_GOURAUD] );
		pMeshInstance->SetWorldMatrix( MATRIX4::translation( VECTOR3( -5.0f + ( i % 5 ) * 2.5f, 0.0f, 2.0f * (  i - 2 ) ) ) );
		//pMeshInstance->SetWorldMatrix( MATRIX4::translation( VECTOR3( -5.0f + 2.0f * i, 0.0f, 0.0f ) ) );
		_lpMeshInstances.push_back( pMeshInstance );
	}

	/*MESH* pPlaneMesh = new MESH();
	pPlaneMesh->Load( &_PlaneMeshData );
	_lpMeshes.push_back( pPlaneMesh );

	MESH_INSTANCE* pPlaneMeshInstance = new MESH_INSTANCE( pPlaneMesh );

	pPlaneMeshInstance->SetShader(_pShaderArray [SHADER_ID_COLOR_PER_VERTEX] );
	_lpMeshInstances.push_back( pPlaneMeshInstance );*/

	MESH* pTorusMesh = new MESH();
	pTorusMesh->LoadObjFile( "../../../Models/test4.obj" );
	_lpMeshes.push_back( pTorusMesh );

	MESH_INSTANCE* pTorusMeshInstance = new MESH_INSTANCE( pTorusMesh );

	//pTorusMeshInstance->SetShader( _pShaderArray[SHADER_ID_COLOR_PER_VERTEX] );
	pTorusMeshInstance->SetShader( _pShaderArray[SHADER_ID_GOURAUD] );
	_lpMeshInstances.push_back( pTorusMeshInstance );

	// Camera
	_pCamera = new CAMERA();
	_pCamera->SetAspectRatio( (float)width() / (float)height() );

	connect( &_MainLoopTimer, SIGNAL( timeout() ), this, SLOT( MainLoop() ) );
	connect( &_FpsTimer, SIGNAL( timeout() ), this, SLOT( FpsTimerLoop() ) );

	_MainLoopTimer.start();
	_FrameTimer.start();
	//_FpsTimer.start( 1000 );

	_uiFramesCount = 0;
}

//-----------------------------------------------------------------------------
//! @brief		resizeGL
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void QtRenderWidget::resizeGL(int w, int h)
{
	glViewport( 0, 0, w, qMax( h, 1 ) );
	_pCamera->SetAspectRatio( (float)width() / (float)height() );
	//updateGL();
}

//-----------------------------------------------------------------------------
//! @brief		paintGL
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void QtRenderWidget::paintGL()
{
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	for	( int i =0; i < _lpMeshInstances.count(); i++ )
	{
		_lpMeshInstances[i]->Draw( _pCamera );
	}
}

//-----------------------------------------------------------------------------
//! @brief		MainLoop
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void	QtRenderWidget::MainLoop()
{
	//int nElapsedTimeInNs = _FrameTimer.nsecsElapsed();
	int nElapsedTime = _FrameTimer.restart();

	_uiFramesCount++;
	_uiFpsTimer += nElapsedTime;

	if	( _uiFpsTimer > 100 )
	{
		float	fFramesPerSecond = (float)_uiFramesCount / (float)_uiFpsTimer * 1000.0f;
		QString s;

		s.sprintf( "%f fps", fFramesPerSecond );
		parentWidget()->parentWidget()->setWindowTitle( s );

		_uiFramesCount	= 0;
		_uiFpsTimer		= 0;
	}

	//qDebug( "Elapsed time: %d ns ( %f ms )", nElapsedTimeInNs, (float)nElapsedTimeInNs / 1000000.0f );

	//qDebug( "Speed vector: %f %f %f", (float)_vCameraTranslationVector.getX(), (float)_vCameraTranslationVector.getY(), (float)_vCameraTranslationVector.getZ() );
	//qDebug( "Camera delta: pitch = %d, head = %d", _nCameraDeltaPitch, _nCameraDeltaHead );

	if	( lengthSqr( _vCameraTranslationVector ) > 0.0f )
	{
		//_pCamera->Translate( _vCameraTranslationVector * ( _fCameraTranslationSpeed * (float)nElapsedTimeInNs / 1000000000.0f ) );
		_pCamera->Translate( _vCameraTranslationVector * ( _fCameraTranslationSpeed * (float)nElapsedTime / 1000.0f ) );
	}

	_nCameraDeltaPitch	= 0;
	_nCameraDeltaHead	= 0;

	updateGL();
}

//-----------------------------------------------------------------------------
//! @brief		keyPressEvent
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void	QtRenderWidget::FpsTimerLoop()
{
	QString s;

	s.sprintf( "%d", _uiFramesCount );
	_uiFramesCount = 0;
	parentWidget()->parentWidget()->setWindowTitle( s );
}

//-----------------------------------------------------------------------------
//! @brief		keyPressEvent
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void	QtRenderWidget::keyPressEvent(QKeyEvent *	event)
{
	switch	( event->key() )
	{
	case Qt::Key_Z:
		_vCameraTranslationVector.setZ( _vCameraTranslationVector.getZ() - 1.0f );
		break;

	case Qt::Key_S:
		_vCameraTranslationVector.setZ( _vCameraTranslationVector.getZ() + 1.0f );
		break;

	case Qt::Key_Q:
		_vCameraTranslationVector.setX( _vCameraTranslationVector.getX() - 1.0f );
		break;

	case Qt::Key_D:
		_vCameraTranslationVector.setX( _vCameraTranslationVector.getX() + 1.0f );
		break;

	case Qt::Key_A:
		_vCameraTranslationVector.setY( _vCameraTranslationVector.getY() + 1.0f );
		break;

	case Qt::Key_E:
		_vCameraTranslationVector.setY( _vCameraTranslationVector.getY() - 1.0f );
		break;

	default:
		QGLWidget::keyPressEvent( event );
		break;
	}
}

//-----------------------------------------------------------------------------
//! @brief		keyReleaseEvent
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void	QtRenderWidget::keyReleaseEvent(QKeyEvent *	event)
{
	switch	( event->key() )
	{
	case Qt::Key_Z:
		_vCameraTranslationVector.setZ( _vCameraTranslationVector.getZ() + 1.0f );
		break;

	case Qt::Key_S:
		_vCameraTranslationVector.setZ( _vCameraTranslationVector.getZ() - 1.0f );
		break;

	case Qt::Key_Q:
		_vCameraTranslationVector.setX( _vCameraTranslationVector.getX() + 1.0f );
		break;

	case Qt::Key_D:
		_vCameraTranslationVector.setX( _vCameraTranslationVector.getX() - 1.0f );
		break;

	case Qt::Key_A:
		_vCameraTranslationVector.setY( _vCameraTranslationVector.getY() - 1.0f );
		break;

	case Qt::Key_E:
		_vCameraTranslationVector.setY( _vCameraTranslationVector.getY() + 1.0f );
		break;

	default:
		QGLWidget::keyReleaseEvent( event );
		break;
	}
}

//-----------------------------------------------------------------------------
//! @brief		mousePressEvent
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void	QtRenderWidget::mousePressEvent(QMouseEvent *	event)
{
	_LastMousePosition = event->pos();
	_nCameraDeltaPitch = 0;
	_nCameraDeltaHead = 0;
	//qDebug( "Mouse press: %f %f", event->posF().x(), event->posF().y() );
}

//-----------------------------------------------------------------------------
//! @brief		mouseReleaseEvent
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void	QtRenderWidget::mouseReleaseEvent(QMouseEvent *	event)
{
	
}

//-----------------------------------------------------------------------------
//! @brief		mouseMoveEvent
//! @details		
//! @param		
//! @return		
//! @author		JJA : 2012-07-19
//-----------------------------------------------------------------------------
void	QtRenderWidget::mouseMoveEvent(QMouseEvent *	event)
{
	//qDebug( "Mouse move: %d %d", event->x(), event->y() );
	_nCameraDeltaPitch = event->y() - _LastMousePosition.y();
	_nCameraDeltaHead = event->x() - _LastMousePosition.x();

	if	( _nCameraDeltaHead != 0 )
	{
		_pCamera->RotateHead( DEGREES_TO_RADIANS( _fCameraRotationSpeed * -_nCameraDeltaHead ) );
	}

	if	( _nCameraDeltaPitch != 0 )
	{
		_pCamera->RotatePitch( DEGREES_TO_RADIANS( _fCameraRotationSpeed * -_nCameraDeltaPitch ) );
	}

	_LastMousePosition = event->pos();
}

//=============================================================================
//	CODE ENDS HERE
//=============================================================================
